/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d.vector;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

/**
 * A generic 2-element tuple that is represented by single-precision floating
 * point x,y coordinates.
 * @author Volker Everts
 * @version 0.1 - 17.09.2011: Created
 * @version 0.2 - 23.11.2011: Reviewed (added Externalizable, Cloneable)
 */
public class Tuple2f implements Externalizable {

    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">

    /** The serial version UID. */
    private static final long serialVersionUID = 20110917L;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Fields ">

    /** The x coordinate. */
    public float x;

    /** The y coordinate. */
    public float y;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Initialization ">

    /**
     * Constructs and initializes a Tuple2f to (0,0).
     */
    public Tuple2f() {

    }

    /**
     * Constructs and initializes a Tuple2f from the specified x y coordinates.
     * @param x the x coordinate
     * @param y the y coordinate
     */
    public Tuple2f(float x, float y) {
        this.x = x;
        this.y = y;
    }

    /**
     * Constructs and initializes a Tuple2f from the specified other Tuple2f.
     * @param t the other tuple
     */
    public Tuple2f(Tuple2f t) {
        x = t.x;
        y = t.y;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Properties ">

    /**
     * Gets the x coordinate.
     * @return the x coordinate
     */
    public final float getX() {
        return x;
    }

    /**
     * Sets the x coordinate.
     * @param x the new x value
     */
    public final void setX(float x) {
        this.x = x;
    }

    /**
     * Gets the y coordinate.
     * @return the y coordinate
     */
    public final float getY() {
        return y;
    }

    /**
     * Sets the y coordinate.
     * @param y the new y value
     */
    public final void setY(float y) {
        this.y = y;
    }

    /**
     * Sets the value to the specified x and y coordinates.
     * @param x the new x value
     * @param y the new y value
     */
    public final void set(float x, float y) {
        this.x = x;
        this.y = y;
    }

    /**
     * Sets the value of this tuple to the value of the specified other tuple.
     * @param t the other tuple
     */
    public final void set(Tuple2f t) {
        x = t.x;
        y = t.y;
    }

    /**
     * Sets the value of this tuple from the data stored in the supplied float
     * array, starting at the specified offset.
     * @param fv the float array containing the x and y coordinates in order
     * @param offset the offset at which the data ist stored in the float array
     */
    public final void set(float[] fv, int offset) {
        x = fv[offset + 0];
        y = fv[offset + 1];
    }

    /**
     * Copies the values of the elements of this tuple into the supplided float
     * array, starting at the specified offset.
     * @param fv the float array that will contain the values of the vector in order x, y
     * @param offset the offset at which the data ist to be stored in the float array
     */
    public final void get(float[] fv, int offset) {
        fv[offset + 0] = x;
        fv[offset + 1] = y;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Methods ">

    /**
     * Adds the specified x and y values to the coordinate values of this tuple:
     * this = this + (x,y).
     * @param x the x coordinate value to add
     * @param y the y coordinate value to add
     */
    public final void add(float x, float y) {
        this.x += x;
        this.y += y;
    }

    /**
     * Sets the value of this tuple to the vector sum of itself and another
     * tuple t: this = this + t.
     * @param t the other tuple
     */
    public final void add(Tuple2f t) {
        x += t.x;
        y += t.y;
    }

    /**
     * Sets the value of this tuple to the vector sum of two other tuples:
     * this = t1 + t2.
     * @param t1 the first tuple
     * @param t2 the second tuple
     */
    public final void add(Tuple2f t1, Tuple2f t2) {
        x = t1.x + t2.x;
        y = t1.y + t2.y;
    }

    /**
     * Subtracts the specified x and y values from the coordinate values of this
     * tuple: this = this - (x,y).
     * @param x the x coordinate value to subtract
     * @param y the y coordinate value to subtract
     */
    public final void sub(float x, float y) {
        this.x -= x;
        this.y -= y;
    }

    /**
     * Sets the value of this tuple to the vector difference of itself and
     * another tuple t: this = this - t.
     * @param t the other tuple
     */
    public final void sub(Tuple2f t) {
        x -= t.x;
        y -= t.y;
    }

    /**
     * Sets the value of this tuple to the vector difference of two other tuples:
     * this = t1 - t2.
     * @param t1 the first tuple
     * @param t2 the second tuple
     */
    public final void sub(Tuple2f t1, Tuple2f t2) {
        x = t1.x - t2.x;
        y = t1.y - t2.y;
    }

    /**
     * Sets the value of this tuple to the scalar multiplication of itself:
     * this = this * s
     * @param s the scalar value
     */
    public final void scale(float s) {
        x *= s;
        y *= s;
    }

    /**
     * Sets the value of this tuple to the scalar multiplication of another
     * tuple t by a scale factor s. The other tuple is not changed: this = t * s
     * @param s the scalar value
     * @param t the other tuple
     */
    public final void scale(float s, Tuple2f t) {
        x = t.x * s;
        y = t.y * s;
    }

    /**
     * Sets the value of this tuple to the scalar multiplication
     * of itself and then adds tuple t: this = s * this + t.
     * @param s the scalar value
     * @param t the tuple to be added
     */
    public final void scaleAdd(float s, Tuple2f t) {
        x = x * s + t.x;
        y = y * s + t.y;
    }

    /**
     * Sets the value of this tuple to the scalar multiplication
     * of tuple t1 and then adds tuple t2: this = s * t1 + t2.
     * @param s the scalar value
     * @param t1 the tuple to be multipled
     * @param t2 the tuple to be added
     */
    public final void scaleAdd(float s, Tuple2f t1, Tuple2f t2) {
        x = t1.x * s + t2.x;
        y = t1.y * s + t2.y;
    }

    /**
     * Negates the value of this tuple in place: this = -this.
     */
    public final void negate() {
    	x = -x;
    	y = -y;
    }

    /**
     * Sets the value of this tuple to the negation of another tuple t:
     * this = -t.
     * @param t the other tuple
     */
    public final void negate(Tuple2f t) {
    	x = -t.x;
    	y = -t.y;
    }

    /**
     * Clamps all values of this tuple to the range [min, max].
     * @param min the lowest value in the tuple coordinates after clamping
     * @param max the highest value in the tuple coordinates after clamping
     */
    public final void clamp(float min, float max) {
        if (x < min) x = min;
        else if (x > max) x = max;
        if (y < min) y = min;
        else if (y > max) y = max;
    }

    /**
     * Clamps another tuple to the range [min, max] and  places the values into
     * this tuple. The other tuple is not changed.
     * @param t the other tuple
     * @param min the lowest coordinate value after clamping
     * @param max the highest cordinate value after clamping
     */
    public final void clamp(Tuple2f t, float min, float max) {
        if (t.x < min) x = min;
        else if (t.x > max) x = max;
        else x = t.x;
        if (t.y < min) y = min;
        else if (t.y > max) y = max;
        else y = t.y;
    }

    /**
     * Returns true if the L-infinite distance between this tuple and another
     * tuple t is less than or equal to the specified positive epsilon parameter.
     * @param t the other tuple
     * @param eps the epsilon parameter
     * @return true, if this tuple is epsilon equal to the other tuple
     */
    public final boolean equals(Tuple2f t, float eps) {
        if (t == this) return true;
        return FMath.isEqual(x, t.x, eps) && FMath.isEqual(y, t.y, eps);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Implementation Object ">

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Point2f) {
            Tuple2f t = (Tuple2f) obj;
            return x == t.x && y == t.y;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + Float.floatToIntBits(x);
        hash = 31 * hash + Float.floatToIntBits(y);
        return hash;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Implementation Externalizable ">

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeFloat(x);
        out.writeFloat(y);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        x = in.readFloat();
        y = in.readFloat();
    }

    // </editor-fold>

} // end class Tuple2f